from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import sys
import math
import time
import copy
import struct
import multiprocessing, signal
import types

import glFreeType


# Some api in the chain is translating the keystrokes to this octal string
# so instead of saying: ESCAPE = 27, we use the following.
ESCAPE = '\033'

TYPE_STARS = 2
TYPE_STAR_PLANETS = 1

PERF_HIGH = 1
PERF_AVG = 2
PERF_LOW = 3

class galaxyvis:
    def __init__(self, width, height, data, performance):
        self.width = width
        self.height = height
        self.window = 0

        self.far = 100000.0
        self.distance = 50000.0

        self.frametime = time.time()
        self.framectr = 0
        self.avgframe = 0.0

        self.data = data
        self.stars = data["stars"]
        self.planets = data["planets"]
        self.moons = data["moons"]
        self.galaxyangle = 0.0


        self.star_highlight_idx = 8
        self.highlight_pad_idx = 7

        self.camerapos = {"dist": 2500, "alphax": 45, "alphay": 45}
        self.cursor = {"x": 0, "y": 0}
        self.prevcursor = {"x": 0, "y": 0}
        self.startidx = 2
        self.highlighted = []
        self.highlightvis = [{"type":0}]
        self.speed = 0.05
        self.colors = [[0.25, 0.6, 1.0], [0.6, 0.8, 1.0], [1.0, 1.0, 1.0], [0.9, 0.9, 0.6], [1.0, 1.0, 0.25], [1.0, 0.7, 0.2], [1.0, 0.15, 0.15], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 1.0, 0.0], [0.0, 1.0, 1.0], [1.0, 0.0, 1.0], [0.5, 0.5, 1.0]]

        self.performance = performance
        self.change_perf_profile(self.performance)
        self.overtime_frames = 0

    def change_perf_profile(self, newperf):
        if newperf <PERF_HIGH or newperf>PERF_LOW:
            newperf = PERF_LOW
        if newperf == PERF_HIGH:
            self.performance = PERF_HIGH
            self.galaxy_list = [self.startidx, self.startidx+1, self.startidx+2, self.startidx+3, self.startidx+4]
            self.point_sizes_list = [20, 10, 5, 1, 0.5]
        elif newperf == PERF_AVG:
            self.performance = PERF_AVG
            self.galaxy_list = [self.startidx, self.startidx+1]
            self.point_sizes_list = [5, 1]
        elif newperf == PERF_LOW:
            self.performance = PERF_LOW
            self.galaxy_list = [self.startidx]
            self.point_sizes_list = [1]

    def decrease_perf(self):
        if self.performance == PERF_HIGH:
            print "switching to average performance profile"
            self.change_perf_profile(PERF_AVG)
            self.create_lists(self.startidx)
        elif self.performance == PERF_AVG:
            print "switching to low performance profile"
            self.change_perf_profile(PERF_LOW)
            self.create_lists(self.startidx)


    def set_colors(self, e, intensity, alpha):
        if e["class"] == "O":
            glColor(0.25*intensity, 0.6*intensity, 1.0*intensity, alpha)
        elif e["class"] == "B":
            glColor(0.6*intensity, 0.8*intensity, 1.0*intensity, alpha)
        elif e["class"] == "A":
            glColor(1.0*intensity, 1.0*intensity, 1.0*intensity, alpha)
        elif e["class"] == "F":
            glColor(0.9*intensity, 0.9*intensity, 0.6*intensity, alpha)
        elif e["class"] == "G":
            glColor(1.0*intensity, 1.0*intensity, 0.25*intensity, alpha)
        elif e["class"] == "K":
            glColor(1.0*intensity, 0.7*intensity, 0.2*intensity, alpha)
        elif e["class"] == "M":
            glColor(1.0*intensity, 0.15*intensity, 0.15*intensity, alpha)

    def create_highlight_list(self, index_pad, index_star):
        if self.highlightvis[0]["type"] == TYPE_STAR_PLANETS:
            glNewList(index_pad, GL_COMPILE)
            glBegin(GL_QUADS)
            glColor(0.5, 0.5, 0.6, 0.8)
            glVertex(-20, -40, 0)
            glVertex(20, -40, 0)
            glVertex(20, -10, 0)
            glVertex(-20, -10, 0)
            glEnd()
            glEndList()
            
            glNewList(index_star, GL_COMPILE)
            glBegin(GL_POINTS)
            e = self.stars[self.highlighted[0]["sid"]]
            intensity = 1.0
            alpha = 0.8
            self.set_colors(e, intensity, alpha)
            glVertex(0, 0, 0)
            glEnd()
            glEndList()
        elif self.highlightvis[0]["type"] == TYPE_STARS:
            glNewList(index_star, GL_COMPILE)
            glBegin(GL_POINTS)
            intensity = 0.5
            alpha = 0.4
            for e in self.highlightvis[1:]:
                self.set_colors(e, intensity, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

    def create_lists(self, idx):
        alpha = 0.1
        print len(self.stars)

        if self.performance == PERF_HIGH:

            glNewList(idx, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 0.1, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

            glNewList(idx+1, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 0.2, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

            glNewList(idx+2, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 0.3, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

            glNewList(idx+3, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 0.4, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

            glNewList(idx+4, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 0.9, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

        elif self.performance == PERF_AVG:
            alpha = 0.5
            glNewList(idx, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 0.4, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

            glNewList(idx+1, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 1.0, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()

        elif self.performance == PERF_LOW:          
            alpha = 1.0
            glNewList(idx, GL_COMPILE)
            glBegin(GL_POINTS)
            for e in self.stars:
                self.set_colors(e, 1.0, alpha)
                glVertex(e["x"], e["y"], e["z"])
            glEnd()
            glEndList()


    def initGL(self):

        self.create_lists(self.startidx)
        self.highlight(self.highlighted)

        glClearColor(0.1, 0.1, 0.15, 1.0)
        glClearDepth(1.0)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_POINT_SMOOTH)
        glHint(GL_POINT_SMOOTH_HINT, GL_NICEST)
        glDisable(GL_DITHER)
        glDisable(GL_DEPTH_TEST)
        glDepthFunc(GL_LEQUAL)

        glEnable(GL_BLEND)

        glPointSize(0.8)

        glShadeModel(GL_SMOOTH)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
#        glOrtho(-self.width, self.width, -self.height, self.height, -0.1, self.far)
        gluPerspective(45.0, float(self.width)/float(self.height), 0.1, self.far)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        self.start = time.time()
#        self.font = glFreeType.font_data ("Test.ttf", 24)

    def highlight(self, highlighted):
        if highlighted == []:
            if self.highlightvis != []:
                self.highlightvis = [{"type":0}]
            return
        if not isinstance(highlighted, list):
            return
        self.highlighted = highlighted
        if len(highlighted) == 1:
            print  highlighted
            if not isinstance(highlighted[0], dict):
                print "highlighted[0] is not instance of dict"
                return
            if not "sid" in highlighted[0]:
                print "highlighted[0] does not have 'sid'"
                return

            def red(prev, e):
                if (not isinstance(e, dict)) or (not isinstance(prev, types.NoneType) and not isinstance(prev, dict) and not isinstance(prev, list)):
                    return None
                if isinstance(prev, dict) and prev["sid"] == highlighted[0]["sid"] and e["sid"] == highlighted[0]["sid"]:
                    return [prev, e]
                elif isinstance(prev, dict) and prev["sid"] == highlighted[0]["sid"] and e["sid"] != highlighted[0]["sid"]:
                    return [prev]
                elif isinstance(prev, list) and e["sid"] == highlighted[0]["sid"]:
                    return prev + [e]
                elif isinstance(prev, list) and e["sid"] != highlighted[0]["sid"]:
                    return prev
                elif isinstance(prev, types.NoneType) and e["sid"] == highlighted[0]["sid"]:
                    return [e]
                else:
                    return None
            self.highlightvis = reduce(red, self.planets)
            if not isinstance(self.highlightvis, types.NoneType):
                def mp(e):
                    print e
                    e["angle"]=0.0
                    e["angleinc"]=e["vo"]/(e["distance"]*10.0)#150000000.0)
                    return
                map(mp, self.highlightvis)
                aescale = 10.0/max(self.highlightvis, key=lambda e: e["distance"])["distance"]
                # 1 - star, planets, 2 - stars
            else:
                self.highlightvis = []
                aescale = 1
            self.highlightvis.insert(0, {"aescale":aescale, "type": TYPE_STAR_PLANETS})
            self.highlightvis.insert(1, self.stars[self.highlighted[0]["sid"]])
            self.create_highlight_list(self.highlight_pad_idx, self.star_highlight_idx)

        else:
            self.highlightvis = []
            self.highlightvis.insert(0, {"type": TYPE_STARS})
            for e in self.highlighted:
                if isinstance(e, dict) and ("sid" in e):
                    self.highlightvis.append(self.stars[e["sid"]])
            self.create_highlight_list(self.highlight_pad_idx, self.star_highlight_idx)
                

    def resizeGLScene(self, width, height):
        if height == 0:
            height = 1
        self.height = height
        self.width = width
        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
#        glOrtho(-self.width, self.width, -self.height, self.height, -0.1, self.far)
        gluPerspective(45.0, float(self.width)/float(self.height), 0.1, self.far)
        glMatrixMode(GL_MODELVIEW)

        glLoadIdentity() # Reset The View
#        
        
    def start(self):
	try:
            GLU_VERSION_1_2
	except:
            print "Need GLU 1.2 to run this demo"
            sys.exit(1)
        print "Hit ESC key to quit."
	glutInit(sys.argv)
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
	glutInitWindowSize(self.width, self.height)
	glutInitWindowPosition(self.width, 0)
	self.window = glutCreateWindow("galaxy vis")
	glutDisplayFunc(self.drawGLScene)
	glutIdleFunc(self.drawGLScene)
	glutReshapeFunc(self.reSizeGLScene)
	# glutKeyboardFunc(self.keyPressed)
        glutMouseFunc(self.mouseFunc)
        glutMotionFunc(self.motionFunc)
        glutSpecialFunc(self.specialFunc)
	self.initGL()
        glutMainLoop()
            
    def specialFunc(self, *args):
        pass

    def mouseFunc(self, *args):
        pass

    def motionFunc(self, *args):
        self.prevcursor["x"] = self.cursor["x"]
        self.prevcursor["y"] = self.cursor["y"]

        self.cursor["x"] = args[0]
        self.cursor["y"] = args[1]

        dy = self.cursor["y"]-self.prevcursor["y"]
        dx = self.cursor["x"]-self.prevcursor["x"]

        if dy<0:
            if dy < -5:
                dy = - 5
            self.camerapos["alphax"]-=dy
        elif dy>0:
            if dy > 5:
                dy = 5
            self.camerapos["alphax"]-=dy
            

        if dx<0:
            if dx < -5:
                dx = - 5
            self.camerapos["alphay"]-=dx
        elif dx>0:
            if dx > 5:
                dx = 5
            self.camerapos["alphay"]-=dx       

    def drawGLScene(self):
        self.start = time.time()
        glBlendFunc(GL_SRC_ALPHA, GL_ONE)

        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45.0, float(self.width)/float(self.height), 0.1, self.far)

        glMatrixMode(GL_MODELVIEW)
#        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glClear(GL_COLOR_BUFFER_BIT)
        glLoadIdentity()

#        gluLookAt(2000, 2000, 2000, 0,0,0, -0.33, -0.33, 0.33)


        glTranslate(0,0,-self.camerapos["dist"])
        glRotate(-self.camerapos["alphay"], 0, 1, 0)
        glRotate(-self.camerapos["alphax"], 1, 0, 0)

        glPushMatrix()
        glRotate(self.galaxyangle, 0, 0, 1)
        self.galaxyangle += self.speed
        if self.galaxyangle>360:
            self.galaxyangle -= 360

        glBegin(GL_LINES)
        glColor3f(1.0, 1.0, 1.0)
        glVertex(0,0,0)
        glVertex(1500,0,0)
        glVertex(0,0,0)
        glVertex(0,1500,0)
        glEnd()
        
        for i, l in enumerate(self.galaxy_list):
            glPointSize(self.point_sizes_list[i])
            glCallList(l)

        if self.highlightvis != []:
            if self.highlightvis[0]["type"] == TYPE_STAR_PLANETS:
                glTranslate(self.highlightvis[1]["x"], self.highlightvis[1]["y"], self.highlightvis[1]["z"])
                glPointSize(12)
                glCallList(self.star_highlight_idx)
                glPointSize(8)
                glCallList(self.star_highlight_idx)

            elif self.highlightvis[0]["type"] == TYPE_STARS:
                glPointSize(12)
                glCallList(self.star_highlight_idx)
                glPointSize(8)
                glCallList(self.star_highlight_idx)

        glPopMatrix()
#        glColor(0.5, 0.5, 0.5, 1.0)

        if self.highlightvis!=[]:
            if self.highlightvis[0]["type"] == TYPE_STAR_PLANETS:
                aescale = self.highlightvis[0]["aescale"]

                glPushMatrix
                glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)
                glMatrixMode(GL_PROJECTION)        
                glLoadIdentity()
                gluPerspective(45.0, float(self.width)/float(self.height), 0.1, 1000)
                #glOrtho(0, self.width, 0, self.height, -0.1, self.far)
                glMatrixMode(GL_MODELVIEW)
                glLoadIdentity()

                glTranslate(0, -10, 0)
                glTranslate(0,0,-100)
                glRotate(30, 1, 0, 0)

                glCallList(self.highlight_pad_idx)

                glPushMatrix()
                glTranslate(0, -25, 0)

                glPushMatrix()
                glPointSize(7)
                glCallList(self.star_highlight_idx)
                glPointSize(5)
                glPopMatrix()

                for i, p in enumerate(self.highlightvis[2:]):
                    glPushMatrix()
                    glRotate(p["angle"], 0, 0, 1)
                    p["angle"]+=p["angleinc"]
                    glBegin(GL_POINTS)
                    glColor(self.colors[i][0], self.colors[i][1], self.colors[i][2], 1.0)
                    glVertex(0, -p["distance"]*aescale-2, 0)
                    glEnd()
                    glPopMatrix()
                glPopMatrix()

        end = time.time()
        dt = end-self.start
        if dt<0.02:
            self.overtime_frames = 0
            time.sleep(0.02-dt)
        else:
            self.overtime_frames = self.overtime_frames + 1
            if self.overtime_frames>100:
                self.decrease_perf();
                self.overtime_frames = 0
                
        self.framectr+=1
        if end - self.frametime>2.0:
            self.avgframe = (end-self.frametime)/(self.framectr)
            print self.framectr, "frames in", end-self.frametime, "avg frame time:", self.avgframe
            self.framectr = 0
            self.frametime = end
